package com.ruoyi.common.gscUtil.StudyUtil.ThreadPoolExecutorUtil;

import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程池+锁 处理高并发场景
 *并发场景 -> 线程池 -> 加锁。这是如影相随，不能单个拆出来用
 *线程池的基本概念是，在应用程序启动时创建一定数量的线程，并将它们保存在线程池中。当需要执行任务时，从线程池中获取一个空闲的线程，
 * 将任务分配给该线程执行。当任务执行完毕后，线程将返回到线程池，可以被其他任务复用。
 *
 * 先使用核心线程处理，核心满了往队列存，队列和核心都满了，创建新线程处理任务，这时候才用最大线程
 */
public class ThreadPoolExecutorExample {
    /**
     * 线程池，就好比公交站,里面的线程就好比公交车；然后new 线程，就好比超哥自己的私家车。
     * 至于使用场景，超哥应该能悟得到了.方便肯定是私家车方便，但是成本也比公交车贵的多
     * 那个抢商品，就好比春节人们回老家；如果大家都用私家车（new 线程），因为高速车道少（cpu配置不够），那就肯定会塞车（cpu100%）；
     * 这时候有两个解决办法：1.要么就把路修大（提高cpu配置） 2.要么就一部分人坐大巴/公交（线程池）
     */
    /**
     *1.线程池，不是由单量的多少来决定的
     * 首先这个场景是抢单，就会有多个，一上来就该用线程池
     *先天场景就决定了需要线程池，与后天的“单量”无关
     * 2.只要涉及到共享资源，然后又有修改的，一上来就上锁，不需要判断
     */


    // 共享资源
    private static int sharedResource = 0;

    // 创建一个可重入锁
    private static final ReentrantLock lock = new ReentrantLock(true); //公平锁会按照线程请求锁的顺序分配锁，而非公平锁允许插队 **默认非公平**
    public static void main(String[] args) {
        // 创建线程池
        int corePoolSize = 2; // 核心线程数
        int maximumPoolSize = 4; // 最大线程数
        long keepAliveTime = 10; // 空闲线程存活时间
        TimeUnit unit = TimeUnit.SECONDS; // 时间单位
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(2); // 任务队列
        ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 线程工厂
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略

        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                corePoolSize,//核心线程数 (corePoolSize)：线程池中保持的最小线程数，即使它们处于空闲状态。
                maximumPoolSize, //最大线程数 (maximumPoolSize)：线程池中允许的最大线程数。
                keepAliveTime,//空闲线程存活时间 (keepAliveTime)：当线程池中的线程数量超过核心线程数时，多余的空闲线程在终止前等待新任务的最长时间。
                unit,//时间单位 (unit)：keepAliveTime 的时间单位。
                workQueue,//任务队列 (workQueue)：用于保存等待执行的任务的阻塞队列。这里使用了 LinkedBlockingQueue，并设置了容量为 2。
                threadFactory,//线程工厂 (threadFactory)：用于创建新线程的工厂。
                handler // 拒绝策略 (handler)：当任务无法被执行时的处理策略。这里使用了 AbortPolicy，即直接抛出 RejectedExecutionException。
        );

        // 提交任务到线程池
        //(1)由于核心线程数为 2，最大线程数为 4，任务队列容量为 2，因此最多可以同时运行 4 个任务（2 个核心线程 + 2 个额外线程）。
        //(2)如果提交的任务数超过 6 个（4 个线程 + 2 个队列），则会触发拒绝策略。
        for (int i = 0; i < 6; i++) {

            final int taskId = i;
            executor.execute(() -> {
                try {
                    incrementSharedResource();//上锁并做业务处理  **只要涉及到共享资源，然后又有修改的，一上来就上锁，不需要判断**
                    System.out.println("Task " + taskId + " is running on thread 线程名 " + Thread.currentThread().getName());
                    Thread.sleep(2000); // 模拟任务执行时间  休眠2秒钟
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        // 关闭线程池
        executor.shutdown();
        try {
            // 等待所有任务完成
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { //最多等你1分钟,还没有完成也给你强制关闭了
                executor.shutdownNow(); // 强制关闭
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
        // 输出最终的共享资源值
        System.out.println("Final value of sharedResource: " + sharedResource);
    }

    // 使用锁来确保线程安全
    private static void incrementSharedResource() {// **只要涉及到共享资源，然后又有修改的，一上来就上锁，不需要判断**
        lock.lock();  // 加锁
        try {
            sharedResource++;  // 操作共享资源
        } finally {
            lock.unlock();  // 释放锁
        }
    }

}